//+--------------------------------------------------------------------
//| Copyright(c) 2012 - an.vuongngocduy					
//| This file is a part of Compact Game Engine (CGE)	
//+--------------------------------------------------------------------
#ifndef __CGE_STRING_H__
#define __CGE_STRING_H__

#include "cgeAllocator.h"
#include "cgeList.h"
#include "cgeCommons.h"

namespace cge
{
	namespace std
	{
		template<class TVal, class TAlc>
		class StringExt;

		/// Provide interface to interact with Unicode string 
		template<class TVal, class TAlc=Allocator<TVal>>
		class StringStd : public StringExt<TVal, TAlc>
		{
		friend StringExt<TVal, TAlc>;

		public:
			/// Default constructor
			StringStd()
				: mContent(null) , mCapacity(0), mUsed(0)
			{
				//Do nothing
			}

			/// Copy constructor
			StringStd(const StringStd& str)
				: mContent(null) , mCapacity(0), mUsed(0)
			{
				*this = str.mContent;
			}

			/// Construct new string from buffer
			StringStd(const TVal* arr)
				: mContent(null) , mCapacity(0), mUsed(0)
			{
				*this = arr;
			}

			/// Convert a signed integer to string
			explicit StringStd(s32 num)
				: mContent(null) , mCapacity(0), mUsed(0)
			{
			}

			/// Convert an unsigned integer to string
			explicit StringStd(u32 num)
				: mContent(null) , mCapacity(0), mUsed(0)
			{
			}

			/// Destructor
			~StringStd()
			{
				Clear();
			}

			/// Return length of current string
			u32 Length() const
			{
				return (u32) (mUsed == 0 ? 0 : mUsed - 1);
			}

			/// Return size of memory allocated for string 
			u32 Capacity() const
			{
				return mCapacity;
			}

			/// Pointer to string data
			const TVal* Chars() const
			{
				return mContent;
			}

			/// Clean up the content of string and release resources
			void Clear()
			{
				if(mContent)
					mAlloc.Deallocate(mContent);
				mContent = null;
				mCapacity = mUsed = 0;
			}

			/// Erase a part of current string content
			StringStd& Remove(s32 idx, s32 len = -1)
			{
				s32 curLen = Length();
				if(idx < curLen && idx >= 0)
				{
					if(len < 0 || len > curLen)
						len = Length();

					s32 pos = idx;
					
					for(;pos + len < curLen; pos++) 
						mContent[pos] = mContent[pos + len];
					
					for(;pos < curLen; pos++, mUsed--)
						mContent[pos] = '\0';
				}
				return *this;
			}

			/// Add new content to the beginning of current string
			StringStd& Prepend(const StringStd& str)
			{
				return Insert(0, str);
			}

			/// Add new content to the end of current string
			StringStd& Append(const StringStd& str)
			{
				return Insert(mUsed, str);
			}

			/// Insert new content at specified position in current string
			StringStd& Insert(s32 idx, const StringStd& str)
			{
				if(str.Length() > 0 && idx < Length() && idx > 0)
				{
					s32 pos = idx;
					s32 total = Length() + str.Length();
					if(choose_storage(total + 1, false, true))
					{
						for(s32 i = Length(); i >= pos; i--)
							mContent[total--] = mContent[i];

						for(s32 i = 0; i < str.Length(); i++, mUsed++)
							mContent[pos++] = str.mContent[i];
					}
				}
				return *this;
			}

			StringStd& Replace(const StringStd& old, const StringStd& src, u32 idx = 0, u32 len = 0xFFFFFFFF)
			{
				//TO DO
				if(mUsed == 0 
				|| len == 0 
				|| old.mUsed == 0 
				|| old.mUsed > mUsed 
				|| old.mUsed > len 
				|| idx > mLegnth)
					return *this;
				
				if(old.mUsed >= src.mUsed)
				{
				}
				else
				{
				}
				
				return *this;
			}

			/// Extract a part of current string content
			StringStd Substr(s32 idx, s32 len = -1 ) const
			{
				if(idx < 0 || idx >= mUsed || len == 0 )
					return StringStd();

				if(idx + len >= Length() || len < 0)
					return StringStd(mContent + idx);
				else
				{
					StringStd str;
					if(str.choose_storage(len + 1, false, false))
					{
						for(u32 i = 0; i < len; i++)
							str.mContent[i] = mContent[idx + i];
						str.mContent[len] = '\0';
						str.mUsed = len + 1;
					}					
					return str;			
				}
			}

			/// Return lower-case version of current string
			StringStd ToLower() const
			{
				StringStd str(*this);
				for(u32 i = 0; i < str.Length(); i++)
					str.mContent[i] += (str.Content[i] > 0x40 && str.Content[i] < 0x5B) ? 0x20 : 0;
				return str;
			}

			/// Return upper-case version of current string
			StringStd ToUpper() const
			{
				StringStd str(*this);
				for(u32 i = 0; i < str.Length(); i++)
					str.mContent[i] -= (str.Content[i] > 0x60 && str.Content[i] < 0x7B) ? 0x20 : 0;
				return str;
			}

			u32 Split(List<StringStd>& result, const List<StringStd>& seps) const
			{
				//TO DO
				return 0;
			}

			/// Return the position of first appearance of specified substring
			s32 IndexOf(const StringStd& str) const
			{
				if(str.mUsed > mUsed)
					return -1;

				if(str.mUsed == mUsed)
					return (*this == str) ? 0 : -1;

				s32 pos = 0;
				while(pos + str.mUsed < mUsed)
				{
					s32 matched = 0;
					for(;matched < str.Length() && str.mContent[matched] == mContent[pos + matched]; matched++);
					
					if(matched == str.Length())
						return pos;
					else
						pos += (matched == 0) ? 1 : matched;
				}
				return -1;
			}

			/// Return the position of last appearance of specified substring
			s32 LastIndexOf(const StringStd& str) const
			{
				if(str.mUsed > mUsed)
					return -1;

				if(str.mUsed == mUsed)
					return (*this == str) ? 0 : -1;
				
				s32 pos = mUsed - 2;
				while(pos >= 0)
				{
					s32 matched = 0;
					for(;matched >= 0 && str.mContent[str.mUsed - matched - 2] == mContent[pos - matched]; matched++);
					if(matched == str.Length())
						return (pos - str.Length() + 1);
					else
						pos -= (matched == 0) ? 1 : matched;
				}
				return -1;
			}

			/// Copy content of buffer into current string
			StringStd& operator = (const TVal* chars)
			{
				if(chars != null) 
				{
					u32 count = 0;
					for(count = 0; chars[count] != '\0'; count++);

					choose_storage(count + 1, false, false);
					for(count = 0; chars[count] != '\0'; count++)
						mContent[count] = chars[count];
					mContent[count] = '\0';
					mUsed = count + 1;
				}
				return *this;
			}

			/// Copy content of specified string into current string
			StringStd& operator = (const StringStd& str)
			{
				return (*this = str.mContent);
			}

			/// Append current string with content of specified string
			StringStd& operator += (const StringStd& str)
			{
				if(str.Length() > 0)
				{
					if(choose_storage(mUsed + str.Length(), false, true))
					{
						mUsed--;
						for(u32 i = 0; i < str.mUsed; i++)
							mContent[mUsed++] = str.mContent[i];
					}
				}
				return *this;
			}

			/// Concatenate 2 string together
			StringStd operator + (const StringStd& str) const
			{
				StringStd str;
				u32 totalLength = Length() + str.Length();
				if(totalLength > 0)
				{
					if(str.choose_storage(totalLength + 1, true, false))
					{
						for(u32 i = 0; i < Length(); i++)
							str.mContent[str.mUsed++] = mContent[i];
						
						for(u32 i = 0; i < str.mUsed; i++)
							str.mContent[str.mUsed++] = str.mContent[i];
					}
				}
				return str;
			}

			/// Check whether 2 strings are have same content or not
			b8 operator == (const StringStd& str) const
			{
				if(mContent == str.mContent)
					return true;

				if(mUsed != str.mUsed)
					return false;

				for(u32 i = 0; i < mUsed; i++)
				{	
					if(mContent[i] != str.mContent[i])
					{
						return false;
					}
				}

				return true;
			}

			/// Check whether 2 strings are different to each other or not
			b8 operator != (const StringStd& str) const
			{
				return !(*this == str);
			}

			/// Get or set a character at specified position
			TVal& operator[](u32 idx)
			{
				return mContent[idx];
			}

			/// Read character value at specified position
			TVal operator[] (u32 idx) const
			{
				return mContent[idx];
			}

		protected:
			b8 choose_storage(u32 capacity, b8 createNew = true, b8 copyToNew = true)
			{
				if(capacity <= mCapacity && !createNew)
					return false;

				TVal* storage = mAlloc.Allocate(capacity);

				if(storage == null) // out of memory
					return false;

				u32 count = 0;
				for(; count < mUsed && count < capacity && copyToNew; count++)
					storage[count] = mContent[count];

				mAlloc.Deallocate(mContent);
				mContent = storage;
				mUsed = count;
				mCapacity = capacity;
				return true;
			}

		protected:
			TAlc mAlloc;
			TVal* mContent;
			s32 mCapacity;
			s32 mUsed;
		};

		typedef StringStd<c8> String;

		typedef StringStd<c16> Stringw;
	}

	namespace std
	{
		/// Template class for String extension
		template<class TVal, class TAlc>
		class StringExt
		{
		protected:
			StringExt(){}
		};

		/// Extent String feature . Support some methods to interact with UTF-8
		template<class TAlc>
		class StringExt<c8, TAlc>
		{
		protected:
			StringExt(){ AsUTF8.thiz = (StringStd<c8, TAlc>*)this; }

		public:
			class 
			{
			friend StringExt<c8, TAlc>;

			public:
				u32 Length() const
				{
					const c8* utf8_buf = thiz->mContent;
					
					u32 len = 0;
					
					u32 uc = get_next_unicode_char(&utf8_buf);

					while(uc)
					{
						len++;
						uc = get_next_unicode_char(&utf8_buf);
					}
					return len;
				}

				u32 CharAt(s32 idx) const
				{
					const c8* utf8_buf = thiz->mContent;
					for(s32 i = 0; true; i++)
					{
						u32 uc = get_next_unicode_char(&utf8_buf);
						if(uc == 0 || i == idx)
							return uc;
					}
				}

				StringStd<c8, TAlc> Substr(s32 idx, s32 len = -1) const
				{
					StringStd<c8, TAlc> str;
					str.choose_storage(10);
					return str;
				}

				StringStd<c8, TAlc> ToLower() const
				{
				}

				StringStd<c8, TAlc> ToUpper() const
				{
				}				

				StringStd<c8,TAlc>& FromUCS2(const c16* wchars)
				{
					return *thiz;
				}

				StringStd<c8,TAlc>& FromUTF16(const c16* utf16)
				{
					return *thiz;
				}

			protected:
				u32 get_next_unicode_char(const c8** buffer) const
				{
					u32 uchar = 0; 
					
					if(*buffer == null) 
						return 0;

					if((u8)(**buffer) <= 0x7F)
					{
						uchar = **buffer;
						*buffer += 1;					
					}
					else if((u8)(**buffer) <= 0xDF)
					{
						uchar = (**buffer & 0x1F) << 6 | *(*buffer + 1) & 0x3F; 
						*buffer += 2;
					}
					else if((u8)(**buffer) <= 0xEF)
					{
						uchar = (**buffer & 0x0F) << 12 | (*(*buffer + 1) & 0x3F) << 6 | *(*buffer + 2) & 0x3F;
						*buffer += 3;
					}
					else if((u8)(**buffer) <= 0xF7)
					{
						uchar = (**buffer & 0x07) << 18 | (*(*buffer + 1) & 0x3F) << 12 
							| (*(*buffer + 2) & 0x3F) << 6 | *(*buffer + 3) & 0x3F;
						*buffer += 4;
					}
					else if((u8)(**buffer) <= 0xFB)
					{
						uchar = (**buffer & 0x03) << 24 | (*(*buffer + 1) & 0x3F) << 18 
							| (*(*buffer + 2) & 0x3F) << 12 | (*(*buffer + 3) & 0x3F) << 6 | *(*buffer + 4) & 0x3F;
						*buffer += 5;
					}
					else if((u8)(**buffer) <= 0xFD)
					{
						uchar = (**buffer & 0x01) << 30 | (*(*buffer + 1) & 0x3F) << 24 
							| (*(*buffer + 2) & 0x3F) << 18 | (*(*buffer + 3) & 0x3F) << 12 
							| (*(*buffer + 4) & 0x3F) << 6  | *(*buffer + 5) & 0x3F;
						*buffer += 6;
					}
					return uchar;
				}

			protected:
				StringStd<c8, TAlc>* thiz;
			
			} AsUTF8;
		};
	}
}

#endif//__CGE_STRING_H__                                              